Repository navigation
Compute the LogitNormal mean by quadrature - #449
Open
alejandroschuler wants to merge 2 commits into
Open
alejandroschuler wants to merge 2 commits into
alejandroschuler wants to merge 2 commits into
Conversation
LogitNormal had no mean_fn, so make_distribution gave it the Monte Carlo fallback, which averages 1000 draws. predict() changed on every call, by up to 0.05 on a small model. The mean of expit(mu + sigma * Z) has no closed form. For sigma <= 1 it now uses 64-node Gauss-Hermite quadrature over Z. That rule alone is off by 3e-3 at sigma = 10, because the integrand is close to a step in Z. For sigma > 1 the mean is E[Phi((mu - L) / sigma)] with L standard logistic, integrated with the trapezoidal rule. Against an mpmath reference the largest error is 6e-15 for sigma from 0.01 to 1000 and mu from -10 to 10. No other built-in distribution uses the fallback. The make_distribution docstring and the SymPy docs now say that the fallback mean is random and that mean_fn avoids it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…elace-ebac6b # Conflicts: # RELEASE_NOTES.md
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
LogitNormalis built withmake_distributionwithoutscipy_dist_clsormean_fn, so it got the factory's Monte Carlo mean,np.mean(self.sample(1000), axis=0). As a result,predict()was random. Two calls on one fitted model (100 rows, 5 trees) gave values up to 0.05 apart. Pickle round-trip checks and other comparisons of predictions were also unreliable (#445 compares predicted parameters for this reason).Change
LogitNormalnow passes a deterministicmean_fn. The mean ofexpit(mu + sigma * Z)withZ ~ N(0, 1)has no closed form. The new function uses one of two quadrature rules:sigma <= 1, 64-node Gauss-Hermite quadrature over Z (hermegauss).sigma > 1,E[Phi((mu - L) / sigma)]with L standard logistic, integrated with the trapezoidal rule (step 0.5 on [-40, 40]).Gauss-Hermite alone is not accurate enough for large sigma. With 64 nodes the error is 3e-3 at sigma = 10 and 3e-2 at sigma = 30, because the integrand is close to a step in Z. The logistic form becomes more accurate as sigma grows, so the two rules complement each other.
The function loops over the nodes and is vectorized over the rows, so its memory grows only with the number of rows.
No other built-in distribution uses the Monte Carlo fallback. Beta and BetaBinomial use scipy, BetaBinomial and BetaBinomialEstN pass
mean_fn, and BetaBernoulli is a classifier. The fallback stays for user distributions. Themake_distributiondocstring anddocs/sympy_distributions.mdnow say that this mean is random and thatmean_fnmakes it deterministic.Tests
tests/test_logitnormal.py(15 tests):predict()calls on a fitted model give equal results;scipy.integrate.quadto 1e-10 on the same grid, and a one-row slice gives the same mean as the full batch.The old Monte Carlo mean fails all 15 tests. A Gauss-Hermite-only mean fails 3 (sigma = 5 and 10).
pytest tests --slow: 184 passed, 2 xfailed, 22 xpassed. black, isort, flake8 and pylint onngboostpass.pylint testsfails with R0801 (duplicate-code) betweentests/test_helpers.pyandtests/test_pickling.py. Both files are unchanged here, so the failure is already on master.🤖 Generated with Claude Code